﻿#include "study14.h"
// C/C++ 标准库学习网站：https://legacy.cplusplus.com
// C/C++ 参考学习网站：https://zh.cppreference.com/w/cpp
// C/C++ 微软(Visual Studio)中文学习网站：https://learn.microsoft.com/zh-cn/cpp/cpp/?view=msvc-170
// OpenGl 学习网站：https://docs.gl/

/*
要使用的正则表达式语法是通过使用一个 std::regex_constants::syntax_option_type 枚举值来指定的。枚举值可以与 std::regex 构造函数一起使用，以指定正则表达式的语法。这些语法在 std::regex_constants 中进行定义：
    ECMAScript： 最接近 JavaScript 和 .NET 语言使用的语法。
    basic：      POSIX basic 正则表达式或 BRE。
    extended：   POSIX extended 正则表达式或 ERE。
    awk：        这是 extended 的，但它具有更多对非打印字符的转义。
    grep：       这是 basic 的，但它也允许换行符 (\n) 字符分隔分支结构。
    egrep：      这是 extended 的，但它也允许换行符字符分隔分支结构。
    默认情况下，如果未指定语法，则假定为 ECMAScript。 只能指定一个语法。

std::regex_constants::syntax_option_type 还可以应用几个标志：
    icase：      匹配时忽略大小写。
    nosubs：     忽略标记的匹配项（即括号中的表达式）；不存储任何替代项。
    optimize：   加快匹配速度，代价是构造时间可能更长。
    collate：    使用区分区域设置的排序规则序列（例如 [a-z] 形式的范围）。
    零个或多个标志可以与语法组合，以指定正则表达式引擎行为。 如果仅指定标志

以 ECMAScript 为例的一些匹配规则：
    .           匹配任何字符，除了换行符。
    \d          匹配任何数字。
    \D          匹配任何非数字字符。
    \s          匹配任何空白字符，包括空格、制表符、换行符。
    \S          匹配任何非空白字符。
    \w          匹配任何字母数字字符，包括下划线。
    \W          匹配任何非字母数字字符。
    ^           匹配字符串的开始。
    $           匹配字符串的结束。
    [...]       匹配括号中的任何字符。
    [^...]      匹配不在括号中的任何字符。
    *           匹配前面的字符零次或多次。
    +           匹配前面的字符一次或多次。
    {n}         匹配前面的字符恰好 n 次。
    {n,}        匹配前面的字符至少 n 次。
    {n,m}       匹配前面的字符至少 n 次，但不超过 m 次；闭区间。
    |           匹配两个或多个分支中的任何一个。
    (...)       标记一个子表达式，并捕获它。
    (?...)      向正则表达式添加条件。
    (?=...)     向正则表达式添加肯定预测。
    (?!...)     向正则表达式添加否定预测。
    (?<=...)    向正则表达式添加前向界定。
    (?<!...)    向正则表达式添加后向界定。
    \b          匹配单词边界。
    \B          匹配非单词边界。
    \A          匹配字符串的开始。
    \Z          匹配字符串的结束，如果是行尾，则不匹配换行符。
    \z          匹配字符串的结束。
    \G          匹配最后匹配完成的位置。
    \cX         匹配由字符 X 命名的控制字符。
    \n          匹配换行符。
    \r          匹配回车符。
    \t          匹配制表符。
    \v          匹配垂直制表符。
    \f          匹配换页符。
    \e          匹配 escape 字符。
    \0          匹配空字符。
    字符类：括号表达式中的字符类用于将命名类中的所有字符都添加到括号表达式所定义的字符集。 若要创建字符类，请使用 [:，后跟字符类的名称，然后再跟 :]。
        [:digit:] 或 [:d:]   匹配位数。
        [:alnum:] 或 [:a:]   匹配任何字母数字字符。
        [:space:] 或 [:s:]   匹配任何空白字符。
        [:lower:]   匹配小写字母。
        [:upper:]   匹配大写字母。
        [:alpha:]   匹配任何字母。
        [:xdigit:]  匹配任何十六进制数字。
        [:print:]   匹配任何可打印字符。
        [:punct:]   匹配任何标点符号。
        [:graph:]   匹配任何可见字符，包括空格。
        [:cntrl:]   匹配任何控制字符。
        [:blank:]   匹配空格或制表符。
        [:word:]    匹配任何单词字符。
        [:xdigit:]  匹配任何十六进制数字。
        [:ascii:]   匹配 ASCII 字符。
        [:^ascii:]  匹配非 ASCII 字符。
        [:^graph:]  匹配不可见字符。
        [:^space:]  匹配非空白字符。
        [:^alnum:]  匹配非字母数字字符。
        [:^alpha:]  匹配非字母字符。
        [:^digit:]  匹配非数字字符。
        [:^upper:]  匹配非大写字母。
        [:^lower:]  匹配非小写字母。
        [:^blank:]  匹配非空格或制表符。
        [:^cntrl:]  匹配非控制字符。
    \uxxxx      匹配 Unicode 字符。
    \Uxxxxxxxx  匹配 Unicode 字符。
    \xhh        匹配两个十六进制数字。
    \uhhhh      匹配四个十六进制数字。
    \cx         匹配控制字符。
    \0ooo       匹配八进制字符。
    \000        匹配空字符。
    八进制转义序列包含一个反斜杠，后跟一个、两个或三个八进制数字 (0-7)
        \123        匹配八进制字符，这里是 ASCII 码的 83 对应 S)。
    \xhhhh      匹配六个十六进制数字。
    \uhhhhhhhh  匹配八个十六进制数字。
    \N{name}    匹配 Unicode 字符名称。
    \p{prop}    匹配 Unicode 字符属性。
    \P{prop}    匹配不在 Unicode 字符属性中的任何字符。

以 ECMAScript 为例的一些示例：
    所有形式的重复计数都可以后跟字符 ?，用于指定非贪婪重复。
    (?:a)           可匹配目标序列 "a"，但 "(?:a)\1" 无效，因为不存在捕获组 1。
    (=a)a           可匹配目标序列 "a"。 肯定断言可匹配目标序列中的初始序列 "a" ，并且正则表达式中的最终 "a" 可匹配目标序列中的初始序列 "a"。
    (!a)a           不匹配目标序列 "a"。
    a\b.            可匹配目标序列 "a~"，但不匹配目标序列 "ab"。
    a\B.            可匹配目标序列 "ab"，但不匹配目标序列 "a~"。
    a*              可匹配目标序列 ""、目标序列 "a"、目标序列 "aa"，以此类推；但不匹配目标序列 "a*"。
    a\*             不匹配目标序列 "aaa"，但可匹配目标序列 "a*"。
    [0-2-]          表示字符集 { 0, 1, 2, - }。
    ab+             可匹配目标序列 "abb"，但不匹配目标序列 "abab"。
    (ab)+           不匹配目标序列 "abb"，但可匹配目标序列 "abab"。
    ((a+)(b+))(c+)  可匹配目标序列 "aabbbc" 并将捕获组 1 与子序列 "aabbb" 相关联，将捕获组 2 与子序列 "aa" 相关联，将捕获组 3 与 "bbb" 相关联，并将捕获组 4 与子序列 "c" 相关联。
    [a^bc]          可匹配目标序列 "a"、"b"、"c" 和 "^"，但不匹配目标序列 "d"。
    [^]abc]         可匹配目标序列 "d"，但不匹配目标序列 "a"、"b"、"c" 或 "]"。
串联：无论是否后跟重复计数，正则表达式都可以串联成较长的正则表达式。 由此产生的表达式可匹配由分别与各个元素相符的序列串联而成的目标序列。
    a{2,3}b             可匹配目标序列 "aab" 和目标序列 "aaab"，但不匹配目标序列 "ab" 或目标序列 "aaaab"。
否定断言：否定断言可匹配除其内容外的任意项。 它不占用目标序列中的任何字符。
    例如，(!aa)(a*)      可匹配目标序列 "a" 并将捕获组 1 与子序列 "a" 相关联。 它不匹配目标序列 "aa" 或目标序列 "aaa"。
非捕获组：非捕获组用于将其内容标记为正则表达式语法中的一个单元，但不会对目标文本设置标签。
    例如，(a)(?:b)*(c)     可匹配目标文本 "abbc"，并将捕获组 1 与子序列 "a" 相关联，将捕获组 2 与子序列 "c" 相关联。
非贪婪重复：非贪婪重复将占用与模式匹配的目标序列的最短子序列；贪婪重复将占用最长子序列。
    例如，(a+)(a*b)    可匹配目标序列 "aaab"。
当使用非贪婪重复时，它会将捕获组 1 与目标序列开头的子序列 "a" 相关联，将捕获组 2 与目标序列末尾的子序列 "aab" 相关联。当使用贪婪匹配时，它会将捕获组 1 与子序列 "aaa" 相关联，将捕获组 2 与子序列 "b" 相关联。
肯定断言：肯定断言可匹配其内容，但不占用目标序列中的任何字符。
示例：
    (=aa)(a*)       可匹配目标序列 "aaaa" 并将捕获组 1 与子序列 "aaaa" 相关联。
    (aa)(a*)        可匹配目标序列 "aaaa"，并将捕获组 1 与目标序列开头的子序列 "aa" 相关联，将捕获组 2 与目标序列末尾的子序列 "aa" 相关联。
    (=aa)(a)|(a)    可匹配目标序列 "a"，并将捕获组 1 与空序列相关联（因为肯定断言失败），将捕获组 2 与子序列 "a" 相关联。 它还可匹配目标序列 "aa"，并将捕获组 1 与子序列 "aa" 相关联，将捕获组 2 与空序列相关联。
*/

/*
std::regex_match 函数用于测试正则表达式是否与整个目标字符串相匹配：搜索正则表达式在目标字符串中的第一次出现测试正则表达式是否与整个目标字符串相匹配。函数原型：
    // (1)
    template <class BidIt, class Alloc, class Elem, class RXtraits, class Alloc2>
    bool regex_match(
        BidIt first,
        Bidit last,
        match_results<BidIt, Alloc>& match,
        const basic_regex<Elem, RXtraits, Alloc2>& re,
        match_flag_type flags = match_default);
    // (2)
    template <class BidIt, class Elem, class RXtraits, class Alloc2>
    bool regex_match(
        BidIt first,
        Bidit last,
        const basic_regex<Elem, RXtraits, Alloc2>& re,
        match_flag_type flags = match_default);
    // (3)
    template <class Elem, class Alloc, class RXtraits, class Alloc2>
    bool regex_match(
        const Elem *ptr,
        match_results<const Elem*, Alloc>& match,
        const basic_regex<Elem, RXtraits, Alloc2>& re,
        match_flag_type flags = match_default);
    // (4)
    template <class Elem, class RXtraits, class Alloc2>
    bool regex_match(
        const Elem *ptr,
        const basic_regex<Elem, RXtraits, Alloc2>& re,
        match_flag_type flags = match_default);
    // (5)
    template <class IOtraits, class IOalloc, class Alloc, class Elem, class RXtraits, class Alloc2>
    bool regex_match(
        const basic_string<Elem, IOtraits, IOalloc>& str,
        match_results<typename basic_string<Elem, IOtraits, IOalloc>::const_iterator, Alloc>& match,
        const basic_regex<Elem, RXtraits, Alloc2>& re,
        match_flag_type flags = match_default);
    // (6)
    template <class IOtraits, class IOalloc, class Elem, class RXtraits, class Alloc2>
    bool regex_match(
        const basic_string<Elem, IOtraits, IOalloc>& str,
        const basic_regex<Elem, RXtraits, Alloc2>& re,
        match_flag_type flags = match_default);
    参数说明：
        BidIt       子匹配项的迭代器类型。 一般情况下，此值为 string::const_iterator、wstring::const_iterator、const char* 或 const wchar_t* 中的一个。
        Alloc       匹配结果分配器类。
        Elem        要匹配的元素的类型。 一般情况下，此值为 string、wstring、char* 或 wchar_t*。
        RXtraits    元素的特征类。
        Alloc2      正则表达式分配器类。
        IOtraits    字符串特征类。
        IOalloc     字符串分配器类。
        flags       匹配标志。
        first       要匹配的序列的开头。
        last        要匹配的序列的结尾。
        match       匹配结果。 对应于 Elem 类型：smatch 对应于 string，wsmatch 对应于 wstring，cmatch 对应于 char* 或 wcmatch 对应于 wchar_t*。
        ptr         指向要匹配的序列开头的指针。 如果 ptr 为 char*，则使用 cmatch 和 regex。 如果 ptr 为 wchar_t*，则使用 wcmatch 和 wregex。
        re          要匹配的正则表达式。 为 string 和 char* 键入 regex，或为 wstring 和 wchar_t* 键入 wregex。
        str         要匹配的字符串。 对应于 Elem 类型。

std::regex_search 函数用于搜索正则表达式在目标字符串中的第一次出现：搜索正则表达式在目标字符串中的第一次出现，并将匹配结果存储在 match_results 对象中。用法和 std::regex_march 函数类似

std::regex_replace 函数用于替换正则表达式匹配项：将正则表达式匹配项替换为指定的字符串。用法和 std::regex_march 函数类似，语法：
    // (1)
    template <class OutIt, class BidIt, class RXtraits, class Alloc, class Elem>
    OutIt regex_replace(
        OutIt out,
        BidIt first,
        BidIt last,
        const basic_regex<Elem, RXtraits, Alloc>& re,
        const basic_string<Elem>& fmt,
        match_flag_type flags = match_default);
    // (2)
    template <class RXtraits, class Alloc, class Elem>
    basic_string<Elem> regex_replace(
        const basic_string<Elem>& str,
        const basic_regex<Elem, RXtraits, Alloc>& re,
        const basic_string<Elem>& fmt,
        match_flag_type flags = match_default);
    相同的参数不再重复；说明如下：
        OutIt out                       输出迭代器类型。 一般情况下，此值为 string::iterator、wstring::iterator、char* 或 wchar_t* 中的一个。
        const basic_string<Elem>& fmt   替换内容的格式。一般情况下，此值为 string 或 wstring。

std::swap 函数用于交换两个正则表达式对象：交换两个正则表达式对象。用法参见官网，这里不做示范。

std::regex_iterator 类用于迭代正则表达式匹配项：迭代正则表达式在目标字符串中的所有出现；语法：
    template<class BidIt,
             class Elem = typename std::iterator_traits<BidIt>::value_type,
             class RxTraits = regex_traits<Elem> >
    class regex_iterator {
    public:
        typedef match_results<BidIt, std::allocator<sub_match<BidIt>>> value_type;
        typedef ptrdiff_t difference_type;
        typedef const value_type* pointer;
        typedef const value_type& reference;
        typedef std::forward_iterator_tag iterator_category;

        regex_iterator();
        regex_iterator(BidIt a, BidIt b, const basic_regex<Elem, RxTraits>& re,
                       match_flag_type flags = match_default);
        regex_iterator(const regex_iterator&);
        regex_iterator& operator=(const regex_iterator&);
        ~regex_iterator();

        const value_type& operator*() const;
        const value_type* operator->() const;
        regex_iterator& operator++();
        regex_iterator operator++(int);
        bool operator==(const regex_iterator&) const;
        bool operator!=(const regex_iterator&) const;
    };

    参数
        BidIt       子匹配项的迭代器类型。
        Elem        要匹配的元素的类型。
        RXtraits    元素的特征类。
    类型名称	                            说明
        difference_type	            迭代器差异的类型。
        iterator_category	        迭代器类别的类型。
        pointer	                    指向一个匹配的指针的类型。
        reference	                对匹配项的引用的类型。
        regex_type	                要匹配的正则表达式类型。
        value_type	                匹配的类型。
    这个类模板描述常量前向迭代器对象。 它将其正则表达式对象 match_results<BidIt> 重复应用到迭代器范围 *pregex 定义的字符序列，以提取 [begin, end)类型的对象。
*/

/*
<regex> 里面定义了 using regex  = basic_regex<char>;
basic_regex 类用于表示正则表达式。它包含以下成员：
    size_type         - 子表达式数目
    const_iterator    - 指向子表达式的第一个元素的迭代器
    const_reference   - 指向子表达式的第一个元素的引用
    str()             - 返回正则表达式的字符串
    begin()           - 返回指向子表达式的第一个元素的迭代器
    end()             - 返回指向子表达式的最后一个元素的迭代器
    operator[]        - 返回指定位置的子表达式
    empty()           - 如果没有子表达式，则返回 true
    size()            - 返回子表达式数目
    swap()            - 交换两个 basic_regex 对象
    assign()          - 赋值一个新的正则表达式
    get_allocator()   - 返回分配器
    set_allocator()   - 设置分配器
    operator=         - 赋值一个新的 basic_regex 对象
    operator==(const basic_regex&) - 比较两个 basic_regex 对象是否相等
    operator!=(const basic_regex&) - 比较两个 basic_regex 对象是否不相等

<regex> 里面定义了 using smatch  = match_results<string::const_iterator>;
match_results 类用于存储匹配结果。它包含以下成员：
    size_type         - 匹配的子序列数目
    const_iterator    - 指向匹配结果的第一个元素的迭代器
    const_reference   - 指向匹配结果的第一个元素的引用
    str()             - 返回匹配结果的字符串
    begin()           - 返回指向匹配结果的第一个元素的迭代器
    end()             - 返回指向匹配结果的最后一个元素的迭代器
    se
    operator[]        - 返回指定位置的匹配项
    prefix()          - 返回目标序列中不包含匹配项的部分
    suffix()          - 返回目标序列中不包含匹配项的部分
    empty()           - 如果没有匹配项，则返回 true
    size()            - 返回匹配项数目
    swap()            - 交换两个 match_results 对象
    matched()         - 返回是否匹配成功
    position()        - 返回第一个匹配项的位置
    length()          - 返回第一个匹配项的长度
    str(size_type i)  - 返回第 i 个匹配项的字符串
    position(size_type i) - 返回第 i 个匹配项的位置
    length(size_type i)  - 返回第 i 个匹配项的长度
    operator==(const match_results&) - 比较两个 match_results 对象是否相等
    operator!=(const match_results&) - 比较两个 match_results 对象是否不相等

enum match_flag_type
{    // specify matching and formatting rules
    match_default = 0x0000,
    match_not_bol = 0x0001,
    match_not_eol = 0x0002,
    match_not_bow = 0x0004,
    match_not_eow = 0x0008,
    match_any = 0x0010,
    match_not_null = 0x0020,
    match_continuous = 0x0040,
    match_prev_avail = 0x0100,
    format_default = 0x0000,
    format_sed = 0x0400,
    format_no_copy = 0x0800,
    format_first_only = 0x1000,
    _Match_not_null = 0x2000
};
匹配选项包括：
    match_not_bol       - 不将目标序列中的第一个位置视为行首
    match_not_eol       - 不将目标序列中超过末尾的位置视为行尾
    match_not_bow       - 不将目标序列中的第一个位置视为单词的开头
    match_not_eow       - 不将目标序列中超过末尾的位置视为单词的结尾
    match_any           - 如果有多个匹配，任何匹配都是可接受的
    match_not_null      - 不要将空的子序列视为匹配项
    match_continuous    - 不搜索除目标序列开头位置之外的匹配项
    match_prev_avail    - 是有效的迭代器，请忽略 --firstmatch_not_bol 和 match_not_bow（如果已设置）
    格式标志包括：
    format_default      -- 使用 ECMAScript 格式规则
    format_sed          -- 使用 sed 格式规则
    format_no_copy      -- 不复制与正则表达式不匹配的文本
    format_first_only   -- 不搜索第一个匹配项之后的匹配项
*/

//#define cout std::cout
//#define endl std::endl

static void regex_num() {
    // 匹配字符串中的数字
    std::string str = "abcDefG 1234 数字中间 567890 字符串";
    std::regex pattern("\\d+",          // 正则表达式：匹配数字串，\d+ 表示至少一个数字
        std::regex_constants::optimize);  // 匹配标志：优化模式
    std::smatch match;
    if (std::regex_search(str,                   // 参数 1：要匹配的序列
        match,                // 参数 2：匹配结果
        pattern,              // 参数 3：正则表达式
        std::regex_constants::match_any))       // 参数 4：匹配标志
        //std::regex_constants::optimize))
        cout << "匹配数字成功：" << match.str() << "   size:" << match.size()
        << "   position:" << match.position() << "   length:" << match.length()
        << "match.prefix().str():" << match.prefix().str() << "   match.suffix().str():" << match.suffix().str() << "matche[0].str():" << match[0].str()
        << "matche[1].str():" << match[1].str() << "matche[2].str():" << match[2].str() << "matche[3].str():" << match[3].str() << endl;
    else
        cout << "匹配数字失败" << endl;
    std::string str_child = str.substr(15, str.size() - 15); // 子串
    std::regex pattern2("\\d+", std::regex_constants::optimize);
    if (std::regex_search(str_child, match, pattern2))
        cout << "子串数字匹配成功：" << match.str() << "   size:" << match.size() << endl;
    else
        cout << "子串数字匹配失败" << endl;

    std::vector<std::string> results;  // 存储所有匹配结果

    std::string::const_iterator search_start(str.cbegin()); // 搜索字符串的开始位置
    while (std::regex_search(search_start, str.cend(), match, pattern)) {
        results.push_back(match.str());     // 保存找到的匹配结果
        search_start = match[0].second;     // 更新搜索开始位置，match[0] 返回一个 sub_match 类型，包含匹配的子序列（是一个 std::pair<const_iterator, const_iterator> 类型）
        //  理解：match[0].second 迭代器指向第一个匹配项的结尾，下次搜索从这个位置开始
    }

    // 输出所有匹配结果
    for (const auto& result : results)
        cout << "匹配结果: " << result << endl;
}

static void regex_word() {
    // 匹配字符串中的英文单词
    std::fstream file("test_copy.txt", std::ios::in);      // 打开文件，只读模式
    std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());  // 读取文件内容
    file.close();
    str += "notaword 1234 数字中间 567890 字符串";
    std::regex pattern(R"(\b\w+\b)", std::regex_constants::icase);  // 正则表达式：匹配单词，\b 表示单词边界，\w+ 表示至少一个字母或数字
    std::smatch match;
    std::string::const_iterator search_start(str.cbegin()); // 搜索字符串的开始位置
    while (std::regex_search(search_start, str.cend(), match, pattern)) {
        std::cout << "匹配单词：" << match.str() << std::endl;
        search_start = match[0].second;     // 更新搜索开始位置，td::regex_search 函数查找失败返回空值，从输出结果可以看到，这种匹配是以标点和空格分割就可以匹配（中文除外）
    }

    // 匹配中文内容
    std::regex pattern_cn(/*R"([\u4e00-\u9fa5]+)"*/ R"([^a-zA-Z0-9\s]+)",
        std::regex_constants::icase);  // 正则表达式：匹配中文单词
    std::smatch match_cn;
    std::string::const_iterator search_start_cn(str.cbegin()); // 搜索字符串的开始位置
    while (std::regex_search(search_start_cn, str.cend(), match_cn, pattern_cn)) {
        std::cout << "匹配中文单词：" << match_cn.str() << std::endl;
        search_start_cn = match_cn[0].second;     // 更新搜索开始位置，td::regex_search 函数查找失败返回空值，从输出结果可以看到，这种匹配是以标点和空格分割就可以匹配（中文除外）
    }
}

static void regex_replace() {
    // 替换字符串中的数字匹配项
    std::string str = "abcDefG 1234 数字中间 567890 字符串";
    std::regex pattern("\\d+");  // 正则表达式：匹配数字串，\d+ 表示至少一个数字
    std::string fmt = "NUM";      // 替换格式：替换为 "NUM"
    std::string result = std::regex_replace(str, pattern, fmt);  // 替换字符串中的数字
    cout << "替换数字成功：" << result << endl;
    cout << "原字符串：" << str << endl;

    // 替换字符串中的所有大写字母
    std::regex pattern2("[A-Z]+");  // 正则表达式：匹配所有大写字母
    std::string fmt2 = "+";      // 替换格式：替换为 "+"
    std::string result2 = std::regex_replace(str, pattern2, fmt2);// 替换字符串中的所有大写字母
    cout << "替换大写字母成功：" << result2 << endl;
    cout << "原字符串：" << str << endl;
}

static void regex_iterator() {
    // 迭代正则表达式匹配项
    std::string str = "abcDefG 1234 数字中间 567890 字符串";
    std::regex pattern("[^a-zA-Z0-9\\s]+");  // 正则表达式：匹配非字母数字非空白字符
    std::regex_iterator<std::string::const_iterator> it(str.cbegin(), str.cend(), pattern);  // 迭代器：迭代数字串
    std::regex_iterator<std::string::const_iterator> end;
    while (it != end) {
        std::cout << "匹配项：" << it->str() << std::endl;
        ++it;
    }
}

static void regex_greed() {
    // 测试贪婪、非贪婪模式
    std::string str = "abcDefG1234kjlk67890字符串";
    std::regex pattern("(.*)(\\d+)");  // 正则表达式：匹配数字前的所有字符，贪婪模式
    std::smatch match;
    if (std::regex_search(str, match, pattern))
        std::cout << "贪婪模式：" << match.str() << "    match[1]:" << match[1].str()
        << "    match[2]:" << match[2].str() << "    match[3]:" << match[3].str()
        << "    match.size：" << match.size() << std::endl;
    std::regex pattern2("(.*?)(\\d+)", std::regex_constants::optimize);// 正则表达式：匹配所有字符，非贪婪模式
    if (std::regex_search(str, match, pattern2))
        std::cout << "非贪婪模式：" << match.str() << "     match[1]:" << match[1].str()
        << "    match[2]:" << match[2].str() << "    match[3]:" << match[3].str()
        << "    match.size：" << match.size() << std::endl;
}

void test_regex() {
    regex_num();
    cout << "-----------------------------------\n";
    regex_word();
    cout << "-----------------------------------\n";
    regex_replace();
    cout << "-----------------------------------\n";
    regex_iterator();
    cout << "-----------------------------------\n";
    regex_greed();
}
